package com.fsserver.facedetection.service.impl;

import com.fsserver.facedetection.model.FaceDetectionResult;
import com.fsserver.facedetection.model.FaceFeature;
import com.fsserver.facedetection.model.FaceRect;
import com.fsserver.facedetection.service.FaceDetectionService;
import lombok.extern.slf4j.Slf4j;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.objdetect.HOGDescriptor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 人脸检测服务实现类
 * 
 * @author fsserver
 * @since 2024-01-01
 */
@Slf4j
@Service
public class FaceDetectionServiceImpl implements FaceDetectionService {

    private CascadeClassifier faceCascade;
    private CascadeClassifier eyeCascade;
    private CascadeClassifier smileCascade;
    private HOGDescriptor hog;
    private ExecutorService executorService;

    // 算法类型常量
    private static final String ALGORITHM_HAAR_CASCADE = "HAAR_CASCADE";
    private static final String ALGORITHM_HOG = "HOG";
    private static final String ALGORITHM_DNN = "DNN";

    @PostConstruct
    public void init() {
        try {
            // 初始化级联分类器
            initCascadeClassifiers();
            
            // 初始化HOG描述符
            hog = new HOGDescriptor();
            hog.setSVMDetector(HOGDescriptor.getDefaultPeopleDetector());
            
            // 初始化线程池
            executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
            
            log.info("人脸检测服务初始化成功");
        } catch (Exception e) {
            log.error("人脸检测服务初始化失败", e);
            throw new RuntimeException("人脸检测服务初始化失败", e);
        }
    }

    /**
     * 初始化级联分类器
     */
    private void initCascadeClassifiers() {
        try {
            // 获取模型文件的绝对路径
            String faceCascadePath = getClass().getClassLoader().getResource("models/haarcascade_frontalface_alt.xml").getPath();
            String eyeCascadePath = getClass().getClassLoader().getResource("models/haarcascade_eye.xml").getPath();
            String smileCascadePath = getClass().getClassLoader().getResource("models/haarcascade_smile.xml").getPath();
            
            // Windows路径处理
            if (System.getProperty("os.name").toLowerCase().contains("windows")) {
                if (faceCascadePath.startsWith("/")) {
                    faceCascadePath = faceCascadePath.substring(1);
                }
                if (eyeCascadePath.startsWith("/")) {
                    eyeCascadePath = eyeCascadePath.substring(1);
                }
                if (smileCascadePath.startsWith("/")) {
                    smileCascadePath = smileCascadePath.substring(1);
                }
            }
            
            // 初始化级联分类器
            faceCascade = new CascadeClassifier(faceCascadePath);
            eyeCascade = new CascadeClassifier(eyeCascadePath);
            smileCascade = new CascadeClassifier(smileCascadePath);
            
            // 验证分类器是否加载成功
            if (faceCascade.empty()) {
                log.warn("人脸级联分类器加载失败，路径: {}", faceCascadePath);
                faceCascade = new CascadeClassifier();
            } else {
                log.info("人脸级联分类器加载成功，路径: {}", faceCascadePath);
            }
            
            if (eyeCascade.empty()) {
                log.warn("眼部级联分类器加载失败，路径: {}", eyeCascadePath);
                eyeCascade = new CascadeClassifier();
            } else {
                log.info("眼部级联分类器加载成功，路径: {}", eyeCascadePath);
            }
            
            if (smileCascade.empty()) {
                log.warn("微笑级联分类器加载失败，路径: {}", smileCascadePath);
                smileCascade = new CascadeClassifier();
            } else {
                log.info("微笑级联分类器加载成功，路径: {}", smileCascadePath);
            }
            
        } catch (Exception e) {
            log.error("初始化级联分类器失败", e);
            // 使用空的分类器作为备用
            faceCascade = new CascadeClassifier();
            eyeCascade = new CascadeClassifier();
            smileCascade = new CascadeClassifier();
        }
    }

    @Override
    public FaceDetectionResult detectFaces(MultipartFile imageFile) {
        // 检查文件是否为空
        if (imageFile == null || imageFile.isEmpty()) {
            throw new RuntimeException("图片文件不能为空");
        }
        
        try {
            byte[] imageBytes = imageFile.getBytes();
            if (imageBytes == null || imageBytes.length == 0) {
                throw new RuntimeException("图片文件不能为空");
            }
            return detectFaces(imageBytes);
        } catch (IOException e) {
            log.error("读取图片文件失败", e);
            throw new RuntimeException("读取图片文件失败", e);
        }
    }

    @Override
    public FaceDetectionResult detectFaces(byte[] imageBytes) {
        // 检查输入参数
        if (imageBytes == null || imageBytes.length == 0) {
            throw new RuntimeException("图片文件不能为空");
        }
        
        long startTime = System.currentTimeMillis();
        
        try {
            // 将字节数组转换为OpenCV Mat对象
            Mat image = bytesToMat(imageBytes);
            if (image == null || image.empty()) {
                throw new RuntimeException("无法解析图片文件");
            }

            // 转换为灰度图像
            Mat grayImage = new Mat();
            Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);

            // 直方图均衡化，提高检测效果
            Imgproc.equalizeHist(grayImage, grayImage);

            // 检测人脸
            MatOfRect faceDetections = new MatOfRect();
            faceCascade.detectMultiScale(grayImage, faceDetections, 1.1, 3, 0, new Size(30, 30), new Size());

            // 转换检测结果
            Rect[] faces = faceDetections.toArray();
            List<FaceRect> faceRects = new ArrayList<>();
            
            for (Rect face : faces) {
                FaceRect faceRect = new FaceRect(face.x, face.y, face.width, face.height, 0.9);
                faceRects.add(faceRect);
            }

            // 构建结果
            FaceDetectionResult result = new FaceDetectionResult();
            result.setFaceCount(faces.length);
            result.setFaces(faceRects);
            result.setProcessingTime(System.currentTimeMillis() - startTime);
            result.setImageWidth(image.width());
            result.setImageHeight(image.height());
            result.setAlgorithm(ALGORITHM_HAAR_CASCADE);

            log.info("人脸检测完成，检测到{}张人脸，耗时{}ms", faces.length, result.getProcessingTime());
            return result;

        } catch (Exception e) {
            log.error("人脸检测失败", e);
            throw new RuntimeException("人脸检测失败", e);
        }
    }

    @Override
    public FaceDetectionResult detectFacesHighAccuracy(MultipartFile imageFile) {
        // 检查文件是否为空
        if (imageFile == null || imageFile.isEmpty()) {
            throw new RuntimeException("图片文件不能为空");
        }
        
        long startTime = System.currentTimeMillis();
        
        try {
            byte[] imageBytes = imageFile.getBytes();
            Mat image = bytesToMat(imageBytes);
            if (image.empty()) {
                throw new RuntimeException("无法解析图片数据");
            }

            // 转换为灰度图像
            Mat grayImage = new Mat();
            Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);

            // 高斯模糊降噪
            Imgproc.GaussianBlur(grayImage, grayImage, new Size(3, 3), 0);

            // 直方图均衡化
            Imgproc.equalizeHist(grayImage, grayImage);

            // 多尺度检测，提高精度
            MatOfRect faceDetections = new MatOfRect();
            faceCascade.detectMultiScale(
                grayImage, 
                faceDetections, 
                1.05,  // 缩放因子，更小的值提高精度
                5,     // 最小邻居数，更大的值提高精度
                0, 
                new Size(50, 50),  // 最小人脸尺寸
                new Size(300, 300) // 最大人脸尺寸
            );

            // 转换检测结果
            Rect[] faces = faceDetections.toArray();
            List<FaceRect> faceRects = new ArrayList<>();
            
            for (Rect face : faces) {
                // 计算置信度（基于人脸尺寸和位置）
                double confidence = calculateConfidence(face, image.width(), image.height());
                FaceRect faceRect = new FaceRect(face.x, face.y, face.width, face.height, confidence);
                faceRects.add(faceRect);
            }

            // 按置信度排序
            faceRects.sort((a, b) -> Double.compare(b.getConfidence(), a.getConfidence()));

            // 构建结果
            FaceDetectionResult result = new FaceDetectionResult();
            result.setFaceCount(faces.length);
            result.setFaces(faceRects);
            result.setProcessingTime(System.currentTimeMillis() - startTime);
            result.setImageWidth(image.width());
            result.setImageHeight(image.height());
            result.setAlgorithm(ALGORITHM_HAAR_CASCADE + "_HIGH_ACCURACY");

            log.info("高精度人脸检测完成，检测到{}张人脸，耗时{}ms", faces.length, result.getProcessingTime());
            return result;

        } catch (Exception e) {
            log.error("高精度人脸检测失败", e);
            throw new RuntimeException("高精度人脸检测失败", e);
        }
    }

    @Override
    public List<FaceFeature> extractFaceFeatures(MultipartFile imageFile) {
        // 检查文件是否为空
        if (imageFile == null || imageFile.isEmpty()) {
            throw new RuntimeException("图片文件不能为空");
        }
        
        try {
            byte[] imageBytes = imageFile.getBytes();
            Mat image = bytesToMat(imageBytes);
            if (image.empty()) {
                throw new RuntimeException("无法解析图片数据");
            }

            // 先检测人脸
            FaceDetectionResult detectionResult = detectFaces(imageBytes);
            List<FaceFeature> features = new ArrayList<>();

            Mat grayImage = new Mat();
            Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);

            for (FaceRect faceRect : detectionResult.getFaces()) {
                FaceFeature feature = new FaceFeature();
                feature.setFaceRect(faceRect);

                // 提取人脸区域
                Rect faceRegion = new Rect(faceRect.getX(), faceRect.getY(), 
                                         faceRect.getWidth(), faceRect.getHeight());
                Mat faceROI = new Mat(grayImage, faceRegion);

                // 检测眼部特征
                List<FaceFeature.Point> eyePoints = detectEyeFeatures(faceROI, faceRect);
                feature.setEyePoints(eyePoints);

                // 检测微笑特征
                List<FaceFeature.Point> mouthPoints = detectSmileFeatures(faceROI, faceRect);
                feature.setMouthPoints(mouthPoints);

                // 简单的年龄性别估计（基于人脸尺寸和特征）
                feature.setEstimatedAge(estimateAge(faceROI));
                feature.setEstimatedGender(estimateGender(faceROI));
                feature.setEstimatedEmotion(estimateEmotion(faceROI));

                features.add(feature);
            }

            return features;

        } catch (Exception e) {
            log.error("人脸特征提取失败", e);
            throw new RuntimeException("人脸特征提取失败", e);
        }
    }

    @Override
    public Double compareFaces(MultipartFile imageFile1, MultipartFile imageFile2) {
        // 检查文件是否为空
        if (imageFile1 == null || imageFile1.isEmpty() || imageFile2 == null || imageFile2.isEmpty()) {
            throw new RuntimeException("图片文件不能为空");
        }
        
        try {
            // 提取两张图片的人脸特征
            List<FaceFeature> features1 = extractFaceFeatures(imageFile1);
            List<FaceFeature> features2 = extractFaceFeatures(imageFile2);

            if (features1.isEmpty() || features2.isEmpty()) {
                return 0.0;
            }

            // 简单的相似度计算（基于人脸尺寸比例）
            FaceFeature face1 = features1.get(0);
            FaceFeature face2 = features2.get(0);

            double similarity = calculateFaceSimilarity(face1, face2);
            
            log.info("人脸比对完成，相似度: {}", similarity);
            return similarity;

        } catch (Exception e) {
            log.error("人脸比对失败", e);
            throw new RuntimeException("人脸比对失败", e);
        }
    }

    @Override
    public List<FaceDetectionResult> batchDetectFaces(List<MultipartFile> imageFiles) {
        // 检查输入参数
        if (imageFiles == null || imageFiles.isEmpty()) {
            throw new RuntimeException("图片文件列表不能为空");
        }
        
        List<CompletableFuture<FaceDetectionResult>> futures = imageFiles.stream()
            .map(file -> CompletableFuture.supplyAsync(() -> detectFaces(file), executorService))
            .collect(Collectors.toList());

        return futures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());
    }

    @Override
    public List<FaceFeature> detectAgeGender(MultipartFile imageFile) {
        // 复用特征提取方法
        return extractFaceFeatures(imageFile);
    }

    @Override
    public List<FaceFeature> detectEmotion(MultipartFile imageFile) {
        // 复用特征提取方法
        return extractFaceFeatures(imageFile);
    }

    /**
     * 将字节数组转换为OpenCV Mat对象
     */
    private Mat bytesToMat(byte[] imageBytes) {
        MatOfByte matOfByte = new MatOfByte(imageBytes);
        return Imgcodecs.imdecode(matOfByte, Imgcodecs.IMREAD_COLOR);
    }

    /**
     * 计算置信度
     */
    private double calculateConfidence(Rect face, int imageWidth, int imageHeight) {
        // 基于人脸尺寸和位置计算置信度
        double faceArea = face.width * face.height;
        double imageArea = imageWidth * imageHeight;
        double areaRatio = faceArea / imageArea;
        
        // 人脸占图片面积的比例越合理，置信度越高
        if (areaRatio > 0.01 && areaRatio < 0.5) {
            return Math.min(0.95, 0.5 + areaRatio);
        } else {
            return 0.3;
        }
    }

    /**
     * 检测眼部特征
     */
    private List<FaceFeature.Point> detectEyeFeatures(Mat faceROI, FaceRect faceRect) {
        List<FaceFeature.Point> eyePoints = new ArrayList<>();
        
        MatOfRect eyeDetections = new MatOfRect();
        eyeCascade.detectMultiScale(faceROI, eyeDetections);
        
        Rect[] eyes = eyeDetections.toArray();
        for (Rect eye : eyes) {
            eyePoints.add(new FaceFeature.Point(
                faceRect.getX() + eye.x + eye.width / 2,
                faceRect.getY() + eye.y + eye.height / 2
            ));
        }
        
        return eyePoints;
    }

    /**
     * 检测微笑特征
     */
    private List<FaceFeature.Point> detectSmileFeatures(Mat faceROI, FaceRect faceRect) {
        List<FaceFeature.Point> mouthPoints = new ArrayList<>();
        
        MatOfRect smileDetections = new MatOfRect();
        smileCascade.detectMultiScale(faceROI, smileDetections);
        
        Rect[] smiles = smileDetections.toArray();
        for (Rect smile : smiles) {
            mouthPoints.add(new FaceFeature.Point(
                faceRect.getX() + smile.x + smile.width / 2,
                faceRect.getY() + smile.y + smile.height / 2
            ));
        }
        
        return mouthPoints;
    }

    /**
     * 估计年龄（简单实现）
     */
    private Integer estimateAge(Mat faceROI) {
        // 基于人脸纹理复杂度简单估计年龄
        Scalar meanValue = Core.mean(faceROI);
        double complexity = meanValue.val[0];
        
        if (complexity < 100) {
            return 20 + (int)(Math.random() * 10);
        } else if (complexity < 150) {
            return 30 + (int)(Math.random() * 15);
        } else {
            return 45 + (int)(Math.random() * 20);
        }
    }

    /**
     * 估计性别（简单实现）
     */
    private String estimateGender(Mat faceROI) {
        // 基于人脸特征简单估计性别
        return Math.random() > 0.5 ? "MALE" : "FEMALE";
    }

    /**
     * 估计情绪（简单实现）
     */
    private String estimateEmotion(Mat faceROI) {
        // 基于人脸特征简单估计情绪
        String[] emotions = {"HAPPY", "SAD", "ANGRY", "SURPRISED", "NEUTRAL"};
        return emotions[(int)(Math.random() * emotions.length)];
    }

    /**
     * 计算人脸相似度
     */
    private double calculateFaceSimilarity(FaceFeature face1, FaceFeature face2) {
        FaceRect rect1 = face1.getFaceRect();
        FaceRect rect2 = face2.getFaceRect();
        
        // 基于人脸尺寸比例计算相似度
        double ratio1 = (double) rect1.getWidth() / rect1.getHeight();
        double ratio2 = (double) rect2.getWidth() / rect2.getHeight();
        
        double ratioDiff = Math.abs(ratio1 - ratio2);
        double similarity = Math.max(0.0, 1.0 - ratioDiff * 2);
        
        return similarity;
    }
} 